﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Mail;
using System.Security.Cryptography.X509Certificates;

namespace NHINDirect
{    
    public class CertificateIndex : ICertificateService, IEnumerable<X509Certificate2>
    {
        IX509Store m_store;
        CertificateDictionary m_certIndex;
        
        public CertificateIndex(IX509Store store)
        {
            if (store == null)
            {
                throw new ArgumentNullException();
            }
            
            this.m_store = store;
            this.Refresh();
        }
        
        public X509Certificate2Collection this[string subjectName]
        {
            get
            {
                CertificateDictionary index = this.m_certIndex;
                
                X509Certificate2Collection matches;
                if (!index.TryGetValue(subjectName, out matches))
                {
                    matches = null;
                }
                return matches;
            }
        }
        
        public IEnumerable<string> Keys
        {
            get
            {
                CertificateDictionary index = this.m_certIndex;
                return index.Keys;
            }
        }

        public X509Certificate2 GetPrivateCertificate(MailAddress address)
        {
            return this.FindCert(address);
        }

        public X509Certificate2 GetPrivateCertificate(string address)
        {
            return this.GetPrivateCertificate(new MailAddress(address));
        }

        public X509Certificate2 GetCertificate(MailAddress address)
        {            
            return this.FindCert(address);
        }

        public X509Certificate2 GetCertificate(string address)
        {
            return this.GetCertificate(new MailAddress(address));
        }

        X509Certificate2 FindCert(MailAddress address)
        {
            if (address == null)
            {
                throw new ArgumentNullException();
            }
            X509Certificate2Collection matches = this[address.Address];
            if (matches == null)
            {
                matches = this[address.Host];
            }
            
            if (matches == null)
            {
                return null;
            }
            return matches.FindUsable();
        }
        
        public void Refresh()
        {
            CertificateDictionary newIndex = this.Load(this.m_store.GetCertificates());
            Interlocked.Exchange(ref this.m_certIndex, newIndex);
        }

        CertificateDictionary Load(IEnumerable<X509Certificate2> certs)
        {
            CertificateDictionary certIndex = new CertificateDictionary();
            if (certs != null)
            {
                foreach (X509Certificate2 cert in certs)
                {
                    string name = cert.ExtractEmailNameOrName();
                    X509Certificate2Collection list = null;
                    if (!certIndex.TryGetValue(name, out list))
                    {
                        list = new X509Certificate2Collection();
                        certIndex[name] = list;
                    }

                    list.Add(cert);
                }
            }
            
            return certIndex;
        }

        public IEnumerator<X509Certificate2> GetEnumerator()
        {
            foreach(X509Certificate2Collection certCollection in this.m_certIndex.Values)
            {
                for (int i = 0, count = certCollection.Count; i < count; ++i)
                {
                    yield return certCollection[i];
                }
            }
        }

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
           return this.GetEnumerator();
        }

        #endregion
    }
    
    internal class CertificateDictionary : Dictionary<string, X509Certificate2Collection>
    {
        internal CertificateDictionary()
            : base(StringComparer.OrdinalIgnoreCase)
        {
        }
    }

}
